
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="zh_Hans">
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>中间件 &#8212; Django 3.2.11.dev 文档</title>
    <link rel="stylesheet" href="../../_static/default.css" type="text/css" />
    <link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
    <script type="text/javascript" id="documentation_options" data-url_root="../../" src="../../_static/documentation_options.js"></script>
    <script type="text/javascript" src="../../_static/jquery.js"></script>
    <script type="text/javascript" src="../../_static/underscore.js"></script>
    <script type="text/javascript" src="../../_static/doctools.js"></script>
    <script type="text/javascript" src="../../_static/language_data.js"></script>
    <link rel="index" title="索引" href="../../genindex.html" />
    <link rel="search" title="搜索" href="../../search.html" />
    <link rel="next" title="如何使用会话" href="sessions.html" />
    <link rel="prev" title="通用视图" href="generic-views.html" />



 
<script src="../../templatebuiltins.js"></script>
<script>
(function($) {
    if (!django_template_builtins) {
       // templatebuiltins.js missing, do nothing.
       return;
    }
    $(document).ready(function() {
        // Hyperlink Django template tags and filters
        var base = "../../ref/templates/builtins.html";
        if (base == "#") {
            // Special case for builtins.html itself
            base = "";
        }
        // Tags are keywords, class '.k'
        $("div.highlight\\-html\\+django span.k").each(function(i, elem) {
             var tagname = $(elem).text();
             if ($.inArray(tagname, django_template_builtins.ttags) != -1) {
                 var fragment = tagname.replace(/_/, '-');
                 $(elem).html("<a href='" + base + "#" + fragment + "'>" + tagname + "</a>");
             }
        });
        // Filters are functions, class '.nf'
        $("div.highlight\\-html\\+django span.nf").each(function(i, elem) {
             var filtername = $(elem).text();
             if ($.inArray(filtername, django_template_builtins.tfilters) != -1) {
                 var fragment = filtername.replace(/_/, '-');
                 $(elem).html("<a href='" + base + "#" + fragment + "'>" + filtername + "</a>");
             }
        });
    });
})(jQuery);</script>

  </head><body>

    <div class="document">
  <div id="custom-doc" class="yui-t6">
    <div id="hd">
      <h1><a href="../../index.html">Django 3.2.11.dev 文档</a></h1>
      <div id="global-nav">
        <a title="Home page" href="../../index.html">Home</a>  |
        <a title="Table of contents" href="../../contents.html">Table of contents</a>  |
        <a title="Global index" href="../../genindex.html">Index</a>  |
        <a title="Module index" href="../../py-modindex.html">Modules</a>
      </div>
      <div class="nav">
    &laquo; <a href="generic-views.html" title="通用视图">previous</a>
     |
    <a href="../index.html" title="使用 Django" accesskey="U">up</a>
   |
    <a href="sessions.html" title="如何使用会话">next</a> &raquo;</div>
    </div>

    <div id="bd">
      <div id="yui-main">
        <div class="yui-b">
          <div class="yui-g" id="topics-http-middleware">
            
  <div class="section" id="s-middleware">
<span id="middleware"></span><h1>中间件<a class="headerlink" href="#middleware" title="永久链接至标题">¶</a></h1>
<p>中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统，用于全局改变 Django 的输入或输出。</p>
<p>每个中间件组件负责做一些特定的功能。例如，Django 包含一个中间件组件 <a class="reference internal" href="../../ref/middleware.html#django.contrib.auth.middleware.AuthenticationMiddleware" title="django.contrib.auth.middleware.AuthenticationMiddleware"><code class="xref py py-class docutils literal notranslate"><span class="pre">AuthenticationMiddleware</span></code></a>，它使用会话将用户与请求关联起来。</p>
<p>他的文档解释了中间件是如何工作的，如何激活中间件，以及如何编写自己的中间件。Django 具有一些内置的中间件，你可以直接使用。它们被记录在 <a class="reference internal" href="../../ref/middleware.html"><span class="doc">built-in middleware reference</span></a> 中。</p>
<div class="section" id="s-writing-your-own-middleware">
<span id="writing-your-own-middleware"></span><h2>编写自己的中间件<a class="headerlink" href="#writing-your-own-middleware" title="永久链接至标题">¶</a></h2>
<p>中间件工厂是一个可调用的程序，它接受 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 可调用并返回中间件。中间件是可调用的，它接受请求并返回响应，就像视图一样。</p>
<p>中间件可以被写成这样的函数：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">simple_middleware</span><span class="p">(</span><span class="n">get_response</span><span class="p">):</span>
    <span class="c1"># One-time configuration and initialization.</span>

    <span class="k">def</span> <span class="nf">middleware</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
        <span class="c1"># Code to be executed for each request before</span>
        <span class="c1"># the view (and later middleware) are called.</span>

        <span class="n">response</span> <span class="o">=</span> <span class="n">get_response</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>

        <span class="c1"># Code to be executed for each request/response after</span>
        <span class="c1"># the view is called.</span>

        <span class="k">return</span> <span class="n">response</span>

    <span class="k">return</span> <span class="n">middleware</span>
</pre></div>
</div>
<p>或者它可以写成一个类，它的实例是可调用的，如下：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">SimpleMiddleware</span><span class="p">:</span>
    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">get_response</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">get_response</span> <span class="o">=</span> <span class="n">get_response</span>
        <span class="c1"># One-time configuration and initialization.</span>

    <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
        <span class="c1"># Code to be executed for each request before</span>
        <span class="c1"># the view (and later middleware) are called.</span>

        <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_response</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>

        <span class="c1"># Code to be executed for each request/response after</span>
        <span class="c1"># the view is called.</span>

        <span class="k">return</span> <span class="n">response</span>
</pre></div>
</div>
<p>Django 提供的 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 响应可能是实际视图（如果这是最后列出的中间件），或者它可能是链中的下一个中间件。不需要知道或关心当前的中间件到底是什么，它只是代表了下一步的内容。</p>
<p>以上是一个轻微的简化——链中最后一个中间件调用的 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 可不是实际视图，而是处理程序的包装方法，它负责应用 <a class="reference internal" href="#view-middleware"><span class="std std-ref">view middleware</span></a>，调用具有适当URL参数的视图，并应用 <a class="reference internal" href="#template-response-middleware"><span class="std std-ref">template-response</span></a> 和 <a class="reference internal" href="#exception-middleware"><span class="std std-ref">exception</span></a> 中间件。</p>
<p>中间件可以只支持同步Python(默认)，或异步Python，或者二者都支持。查看 <a class="reference internal" href="#async-middleware"><span class="std std-ref">异步支持</span></a> 来了解如何通知你支持的是什么以及如何知道你是哪种请求。</p>
<p>中间件可以放在 Python 路径上的任何地方。</p>
<div class="section" id="s-init-get-response">
<span id="init-get-response"></span><h3><code class="docutils literal notranslate"><span class="pre">__init__(get_response)</span></code><a class="headerlink" href="#init-get-response" title="永久链接至标题">¶</a></h3>
<p>中间件工厂必须接受 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 参数。还可以初始化中间件的一些全局状态。记住两个注意事项：</p>
<ul class="simple">
<li>Django仅用 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 参数初始化您的中间件，因此不能定义 <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> ，因为需要其他参数。</li>
<li>与每次请求调用 <code class="docutils literal notranslate"><span class="pre">__call__()</span></code> 方法不同，当 Web 服务器启动时，<code class="docutils literal notranslate"><span class="pre">__init__()</span></code> 只被称为*一次*。</li>
</ul>
</div>
<div class="section" id="s-marking-middleware-as-unused">
<span id="marking-middleware-as-unused"></span><h3>标记未使用的中间件<a class="headerlink" href="#marking-middleware-as-unused" title="永久链接至标题">¶</a></h3>
<p>在启动时确定是否应该使用一个中间件有时是有用的。在这些情况下，您的中间件的 <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> 方法可能会引发 <a class="reference internal" href="../../ref/exceptions.html#django.core.exceptions.MiddlewareNotUsed" title="django.core.exceptions.MiddlewareNotUsed"><code class="xref py py-exc docutils literal notranslate"><span class="pre">MiddlewareNotUsed</span></code></a>。Django 将从中间件进程中删除该中间件，并将调试消息记录到 <a class="reference internal" href="../logging.html#django-request-logger"><span class="std std-ref">django.request</span></a> 日志：设置 <a class="reference internal" href="../../ref/settings.html#std:setting-DEBUG"><code class="xref std std-setting docutils literal notranslate"><span class="pre">DEBUG</span></code></a> 为 <code class="docutils literal notranslate"><span class="pre">True</span></code>。</p>
</div>
</div>
<div class="section" id="s-activating-middleware">
<span id="activating-middleware"></span><h2>激活中间件<a class="headerlink" href="#activating-middleware" title="永久链接至标题">¶</a></h2>
<p>若要激活中间件组件，请将其添加到 Django 设置中的 <a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a> 列表中。</p>
<p>在 <a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a> 中，每个中间件组件由字符串表示：指向中间件工厂的类或函数名的完整 Python 路径。例如，这里创建的默认值是 <a class="reference internal" href="../../ref/django-admin.html#django-admin-startproject"><code class="xref std std-djadmin docutils literal notranslate"><span class="pre">django-admin</span> <span class="pre">startproject</span></code></a>：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">MIDDLEWARE</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s1">&#39;django.middleware.security.SecurityMiddleware&#39;</span><span class="p">,</span>
    <span class="s1">&#39;django.contrib.sessions.middleware.SessionMiddleware&#39;</span><span class="p">,</span>
    <span class="s1">&#39;django.middleware.common.CommonMiddleware&#39;</span><span class="p">,</span>
    <span class="s1">&#39;django.middleware.csrf.CsrfViewMiddleware&#39;</span><span class="p">,</span>
    <span class="s1">&#39;django.contrib.auth.middleware.AuthenticationMiddleware&#39;</span><span class="p">,</span>
    <span class="s1">&#39;django.contrib.messages.middleware.MessageMiddleware&#39;</span><span class="p">,</span>
    <span class="s1">&#39;django.middleware.clickjacking.XFrameOptionsMiddleware&#39;</span><span class="p">,</span>
<span class="p">]</span>
</pre></div>
</div>
<p>Django 安装不需要任何中间件——如果您愿意的话，<a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a> 可以为空——但是强烈建议您至少使用 <a class="reference internal" href="../../ref/middleware.html#django.middleware.common.CommonMiddleware" title="django.middleware.common.CommonMiddleware"><code class="xref py py-class docutils literal notranslate"><span class="pre">CommonMiddleware</span></code></a>。</p>
<p><a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a> 的顺序很重要，因为中间件会依赖其他中间件。例如：类 <a class="reference internal" href="../../ref/middleware.html#django.contrib.auth.middleware.AuthenticationMiddleware" title="django.contrib.auth.middleware.AuthenticationMiddleware"><code class="xref py py-class docutils literal notranslate"><span class="pre">AuthenticationMiddleware</span></code></a> 在会话中存储经过身份验证的用户；因此，它必须在 <a class="reference internal" href="../../ref/middleware.html#django.contrib.sessions.middleware.SessionMiddleware" title="django.contrib.sessions.middleware.SessionMiddleware"><code class="xref py py-class docutils literal notranslate"><span class="pre">SessionMiddleware</span></code></a> 后面运行 。中间件。Session中间件。请参阅 <a class="reference internal" href="../../ref/middleware.html#middleware-ordering"><span class="std std-ref">中间件顺序</span></a> ，用于一些关于 Django 中间件类排序的常见提示。</p>
</div>
<div class="section" id="s-middleware-order-and-layering">
<span id="middleware-order-and-layering"></span><h2>中间件顺序与分层<a class="headerlink" href="#middleware-order-and-layering" title="永久链接至标题">¶</a></h2>
<p>在请求阶段，在调用视图之前，Django 按照定义的顺序应用中间件 <a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a>，自顶向下。</p>
<p>你可以把它想象成一个洋葱：每个中间件类都是一个“层”，它覆盖了洋葱的核心。如果请求通过洋葱的所有层（每一个调用 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> ）以将请求传递到下一层，一直到内核的视图，那么响应将在返回的过程中通过每个层（以相反的顺序）。</p>
<p>如果其中一层决定停止并返回响应而不调用get_response，那么该层(包括视图)中的洋葱层都不会看到请求或响应。响应将只通过请求传入的相同层返回。</p>
</div>
<div class="section" id="s-other-middleware-hooks">
<span id="other-middleware-hooks"></span><h2>其他中间件钩子<a class="headerlink" href="#other-middleware-hooks" title="永久链接至标题">¶</a></h2>
<p>除前面说书的基础请求/响应中间件模式外，你可以给基于类的中间件添加三种其他特殊方法：</p>
<div class="section" id="s-process-view">
<span id="s-view-middleware"></span><span id="process-view"></span><span id="view-middleware"></span><h3><code class="docutils literal notranslate"><span class="pre">process_view()</span></code><a class="headerlink" href="#process-view" title="永久链接至标题">¶</a></h3>
<dl class="method">
<dt id="process_view">
<code class="descname">process_view</code>(<em>request</em>, <em>view_func</em>, <em>view_args</em>, <em>view_kwargs</em>)<a class="headerlink" href="#process_view" title="永久链接至目标">¶</a></dt>
<dd></dd></dl>

<p><code class="docutils literal notranslate"><span class="pre">request</span></code> 是一个 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpRequest" title="django.http.HttpRequest"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpRequest</span></code></a> 对象。<code class="docutils literal notranslate"><span class="pre">view_func</span></code> 是一个 Django 将要使用的 Python 函数。（这是一个真实的函数对象，不是函数的名称）；<code class="docutils literal notranslate"><span class="pre">view_args</span></code> 是一个用来传递给视图的位置参数列表，；<code class="docutils literal notranslate"><span class="pre">view_kwargs</span></code> 是一个用来传递给视图的关键字参数字典。<code class="docutils literal notranslate"><span class="pre">view_args</span></code> 和 <code class="docutils literal notranslate"><span class="pre">view_kwargs</span></code> 都不包含第一个视图参数 ( <code class="docutils literal notranslate"><span class="pre">request</span></code> )。</p>
<p><code class="docutils literal notranslate"><span class="pre">process_view()</span></code> 只在 Django 调用视图前被调用。</p>
<p>它应该返回 <code class="docutils literal notranslate"><span class="pre">None</span></code> 或 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpResponse" title="django.http.HttpResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpResponse</span></code></a> 对象。如果它返回 <code class="docutils literal notranslate"><span class="pre">None</span></code> ，Django 将继续处理这个请求，执行任何其他的 <code class="docutils literal notranslate"><span class="pre">process_view()</span></code> ，然后执行相应的视图。如果它返回 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpResponse" title="django.http.HttpResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpResponse</span></code></a> 对象，Django 不会去影响调用相应的视图；它会将响应中间件应用到 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpResponse" title="django.http.HttpResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpResponse</span></code></a> 并返回结果。</p>
<div class="admonition note">
<p class="first admonition-title">注解</p>
<p>在视图运行前或在 <code class="docutils literal notranslate"><span class="pre">process_view()</span></code> 内访问中间件里的 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpRequest.POST" title="django.http.HttpRequest.POST"><code class="xref py py-attr docutils literal notranslate"><span class="pre">request.POST</span></code></a> 将阻止中间件之后运行的任何视图修改请求的上传处理程序 （<a class="reference internal" href="file-uploads.html#modifying-upload-handlers-on-the-fly"><span class="std std-ref">modify the upload handlers for the request</span></a>  ），通常应该避免这样。</p>
<p class="last"><a class="reference internal" href="../../ref/middleware.html#django.middleware.csrf.CsrfViewMiddleware" title="django.middleware.csrf.CsrfViewMiddleware"><code class="xref py py-class docutils literal notranslate"><span class="pre">CsrfViewMiddleware</span></code></a> 类可以被视为一个例外，因为它提供 <a class="reference internal" href="../../ref/csrf.html#django.views.decorators.csrf.csrf_exempt" title="django.views.decorators.csrf.csrf_exempt"><code class="xref py py-func docutils literal notranslate"><span class="pre">csrf_exempt()</span></code></a> 和 <a class="reference internal" href="../../ref/csrf.html#django.views.decorators.csrf.csrf_protect" title="django.views.decorators.csrf.csrf_protect"><code class="xref py py-func docutils literal notranslate"><span class="pre">csrf_protect()</span></code></a> 装饰器，它们允许视图完全控制 CSRF 验证在什么时候进行。</p>
</div>
</div>
<div class="section" id="s-process-exception">
<span id="s-exception-middleware"></span><span id="process-exception"></span><span id="exception-middleware"></span><h3><code class="docutils literal notranslate"><span class="pre">process_exception()</span></code><a class="headerlink" href="#process-exception" title="永久链接至标题">¶</a></h3>
<dl class="method">
<dt id="process_exception">
<code class="descname">process_exception</code>(<em>request</em>, <em>exception</em>)<a class="headerlink" href="#process_exception" title="永久链接至目标">¶</a></dt>
<dd></dd></dl>

<p><code class="docutils literal notranslate"><span class="pre">request</span></code> 是一个 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpRequest" title="django.http.HttpRequest"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpRequest</span></code></a> 对象。 <code class="docutils literal notranslate"><span class="pre">exception</span></code> 是一个由视图函数引发的 <code class="docutils literal notranslate"><span class="pre">Exception</span></code> 对象。</p>
<p>当视图引发异常时，Django 会调用 <code class="docutils literal notranslate"><span class="pre">process_exception()</span></code>。<code class="docutils literal notranslate"><span class="pre">process_exception()</span></code> 应该返回 <code class="docutils literal notranslate"><span class="pre">None</span></code> 或 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpResponse" title="django.http.HttpResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpResponse</span></code></a> 对象。如果它返回一个 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpResponse" title="django.http.HttpResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpResponse</span></code></a> 对象，模板响应和响应中间件将被应用且会将结果响应返回浏览器。否则，就会开始默认异常处理（ <a class="reference internal" href="../../ref/views.html#error-views"><span class="std std-ref">default exception handling</span></a> ）。</p>
<p>再次，中间件在响应阶段会按照相反的顺序运行，其中包括 <code class="docutils literal notranslate"><span class="pre">process_exception</span></code> 。如果异常中间件返回一个响应，那么中间件之上的中间件类的 <code class="docutils literal notranslate"><span class="pre">process_exception</span></code> 方法根本不会被调用。</p>
</div>
<div class="section" id="s-process-template-response">
<span id="s-template-response-middleware"></span><span id="process-template-response"></span><span id="template-response-middleware"></span><h3><code class="docutils literal notranslate"><span class="pre">process_template_response()</span></code><a class="headerlink" href="#process-template-response" title="永久链接至标题">¶</a></h3>
<dl class="method">
<dt id="process_template_response">
<code class="descname">process_template_response</code>(<em>request</em>, <em>response</em>)<a class="headerlink" href="#process_template_response" title="永久链接至目标">¶</a></dt>
<dd></dd></dl>

<p><code class="docutils literal notranslate"><span class="pre">request</span></code> 是一个 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpRequest" title="django.http.HttpRequest"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpRequest</span></code></a> 对象。<code class="docutils literal notranslate"><span class="pre">response</span></code> 是 <a class="reference internal" href="../../ref/template-response.html#django.template.response.TemplateResponse" title="django.template.response.TemplateResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">TemplateResponse</span></code></a> 对象（或者等效对象），它通过 Django 视图或中间件返回。</p>
<p><code class="docutils literal notranslate"><span class="pre">process_template_response()</span></code> 在视图被完全执行后调用，如果响应实例有 <code class="docutils literal notranslate"><span class="pre">render()</span></code> 方法，表明它是一个 <a class="reference internal" href="../../ref/template-response.html#django.template.response.TemplateResponse" title="django.template.response.TemplateResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">TemplateResponse</span></code></a> 或等效对象。</p>
<p>它必须返回一个实现了 <code class="docutils literal notranslate"><span class="pre">render</span></code> 方法的响应对象。它可以通过改变``response.template_name`` 和 <code class="docutils literal notranslate"><span class="pre">response.context_data</span></code> 来改变给定的 <code class="docutils literal notranslate"><span class="pre">response</span></code> ，或者它可以创建和返回全新的 <a class="reference internal" href="../../ref/template-response.html#django.template.response.TemplateResponse" title="django.template.response.TemplateResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">TemplateResponse</span></code></a> 或等效对象。</p>
<p>你不需要显式地渲染响应——一旦所有模板中间件被调用，响应会被自动渲染。</p>
<p>中间件会在响应阶段按照相反的顺序运行，其中包括 <code class="docutils literal notranslate"><span class="pre">process_template_response()</span></code> 。</p>
</div>
</div>
<div class="section" id="s-dealing-with-streaming-responses">
<span id="dealing-with-streaming-responses"></span><h2>处理流式响应<a class="headerlink" href="#dealing-with-streaming-responses" title="永久链接至标题">¶</a></h2>
<p>与 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpResponse" title="django.http.HttpResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpResponse</span></code></a> 不同，<a class="reference internal" href="../../ref/request-response.html#django.http.StreamingHttpResponse" title="django.http.StreamingHttpResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">StreamingHttpResponse</span></code></a> 没有 <code class="docutils literal notranslate"><span class="pre">content</span></code> 属性。因此，中间件不能再假设所有响应有 <code class="docutils literal notranslate"><span class="pre">content</span></code> 属性。如果它们需要访问 content，它们必须为流式响应进行测试，并且调整其行为：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">streaming</span><span class="p">:</span>
    <span class="n">response</span><span class="o">.</span><span class="n">streaming_content</span> <span class="o">=</span> <span class="n">wrap_streaming_content</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">streaming_content</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
    <span class="n">response</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="n">alter_content</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">content</span><span class="p">)</span>
</pre></div>
</div>
<div class="admonition note">
<p class="first admonition-title">注解</p>
<p><code class="docutils literal notranslate"><span class="pre">streaming_content</span></code> 被假设为体积太大而无法在内存中保存。响应中间件可以将其包装在一个新的生成器里，但不能使用它。包装通常如下实现：</p>
<div class="last highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">wrap_streaming_content</span><span class="p">(</span><span class="n">content</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">content</span><span class="p">:</span>
        <span class="k">yield</span> <span class="n">alter_content</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="s-exception-handling">
<span id="exception-handling"></span><h2>异常处理<a class="headerlink" href="#exception-handling" title="永久链接至标题">¶</a></h2>
<p>Django 自动转换视图引发的异常，或者带有错误状态代码的特定 HTTP 响应内的中间件引发的异常。某些异常（ <a class="reference internal" href="../../ref/views.html#error-views"><span class="std std-ref">Certain exceptions</span></a> ）被转换为 4xx 状态代码，而未知异常被转换为 500 状态代码。</p>
<p>这个变换发生在每个中间件的前后（你可以把它想象成洋葱每层中间的薄膜），因此每个中间件总是可以依赖于从调用它的 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 回调中获得某种类型的HTTP响应。中间件不需要担心它们对 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 的调用包装在 <code class="docutils literal notranslate"><span class="pre">try/except</span></code> 里，也不需要担心处理稍后的中间件或视图引发的异常。即使链中的下一个中间件引发了 <a class="reference internal" href="views.html#django.http.Http404" title="django.http.Http404"><code class="xref py py-class docutils literal notranslate"><span class="pre">Http404</span></code></a> 异常，比如中间件不会查看异常，相反，它会得到一个带有404 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpResponse.status_code" title="django.http.HttpResponse.status_code"><code class="xref py py-attr docutils literal notranslate"><span class="pre">status_code</span></code></a> 的 <a class="reference internal" href="../../ref/request-response.html#django.http.HttpResponse" title="django.http.HttpResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">HttpResponse</span></code></a> 对象。</p>
<p>You can set <a class="reference internal" href="../../ref/settings.html#std:setting-DEBUG_PROPAGATE_EXCEPTIONS"><code class="xref std std-setting docutils literal notranslate"><span class="pre">DEBUG_PROPAGATE_EXCEPTIONS</span></code></a> to <code class="docutils literal notranslate"><span class="pre">True</span></code> to skip this
conversion and propagate exceptions upwards.</p>
</div>
<div class="section" id="s-asynchronous-support">
<span id="s-async-middleware"></span><span id="asynchronous-support"></span><span id="async-middleware"></span><h2>异步支持<a class="headerlink" href="#asynchronous-support" title="永久链接至标题">¶</a></h2>
<div class="versionadded">
<span class="title">New in Django 3.1.</span> </div>
<p>中间件支持同步和异步请求的任意组合。如果Django不能同时支持它们，它会调整请求来适应中间件的需求，但会有性能损失。</p>
<p>默认情况下，Django假设你的中间件只能处理同步请求。如果要改变这种模式，需要在你的中间件工厂函数或类中添加入如下属性：</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">sync_capable</span></code> 是一个布尔值，来表明中间件是否处理同步请求。默认为 <code class="docutils literal notranslate"><span class="pre">True</span></code>。</li>
<li><code class="docutils literal notranslate"><span class="pre">async_capable</span></code> 是一个布尔值，来表明中间件是否处理异步请求。默认为 <code class="docutils literal notranslate"><span class="pre">False</span></code>。</li>
</ul>
<p>如果中间件的``sync_capable = True`` 并且 <code class="docutils literal notranslate"><span class="pre">async_capable</span> <span class="pre">=</span> <span class="pre">True</span></code>，那么Django 会将请求传递给它而不进行转换。在这种情况下，你可以使用 <a class="reference external" href="https://docs.python.org/3/library/asyncio-task.html#asyncio.iscoroutinefunction" title="(在 Python v3.10)"><code class="xref py py-func docutils literal notranslate"><span class="pre">asyncio.iscoroutinefunction()</span></code></a> 来检查传递的 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 是否是一个协同函数，从而确定中间件是否接收到异步请求。</p>
<p><code class="docutils literal notranslate"><span class="pre">django.utils.decorators</span></code> 模块包含 <a class="reference internal" href="../../ref/utils.html#django.utils.decorators.sync_only_middleware" title="django.utils.decorators.sync_only_middleware"><code class="xref py py-func docutils literal notranslate"><span class="pre">sync_only_middleware()</span></code></a>，<a class="reference internal" href="../../ref/utils.html#django.utils.decorators.async_only_middleware" title="django.utils.decorators.async_only_middleware"><code class="xref py py-func docutils literal notranslate"><span class="pre">async_only_middleware()</span></code></a> 和 <a class="reference internal" href="../../ref/utils.html#django.utils.decorators.sync_and_async_middleware" title="django.utils.decorators.sync_and_async_middleware"><code class="xref py py-func docutils literal notranslate"><span class="pre">sync_and_async_middleware()</span></code></a> 装饰器，允许你将这些标志应用到中间件工厂函数中。</p>
<p>返回的可调用对象必须符合 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 方法的同步或异步性质。如果你有一个异步的 <code class="docutils literal notranslate"><span class="pre">get_response</span></code>，你必须返回一个协程函数（<code class="docutils literal notranslate"><span class="pre">async</span> <span class="pre">def</span></code>）。</p>
<p><code class="docutils literal notranslate"><span class="pre">process_view</span></code>、<code class="docutils literal notranslate"><span class="pre">process_template_response</span></code> 和 <code class="docutils literal notranslate"><span class="pre">process_exception</span></code> 方法，如果有的话，也应该进行调整以匹配同步／异步模式。然而，如果你不这样做，Django 会根据需要单独调整它们，但会有额外的性能损失。</p>
<p>下面以一个例子来说明如何创建一个支持这两种功能的中间件函数：</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">django.utils.decorators</span> <span class="kn">import</span> <span class="n">sync_and_async_middleware</span>

<span class="nd">@sync_and_async_middleware</span>
<span class="k">def</span> <span class="nf">simple_middleware</span><span class="p">(</span><span class="n">get_response</span><span class="p">):</span>
    <span class="c1"># One-time configuration and initialization goes here.</span>
    <span class="k">if</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">iscoroutinefunction</span><span class="p">(</span><span class="n">get_response</span><span class="p">):</span>
        <span class="k">async</span> <span class="k">def</span> <span class="nf">middleware</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
            <span class="c1"># Do something here!</span>
            <span class="n">response</span> <span class="o">=</span> <span class="k">await</span> <span class="n">get_response</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">response</span>

    <span class="k">else</span><span class="p">:</span>
        <span class="k">def</span> <span class="nf">middleware</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
            <span class="c1"># Do something here!</span>
            <span class="n">response</span> <span class="o">=</span> <span class="n">get_response</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">response</span>

    <span class="k">return</span> <span class="n">middleware</span>
</pre></div>
</div>
<div class="admonition note">
<p class="first admonition-title">注解</p>
<p>如果你声明了一个同时支持同步和异步调用的混合中间件，你得到的调用种类可能与底层视图不匹配。Django 会优化中间件调用栈，使其尽可能少的同步／异步转换。</p>
<p class="last">因此，即使你包装的是一个异步视图，如果在你和视图之间有其他的、同步的中间件，你也可能会在同步模式下被调用。</p>
</div>
</div>
<div class="section" id="s-upgrading-pre-django-1-10-style-middleware">
<span id="s-upgrading-middleware"></span><span id="upgrading-pre-django-1-10-style-middleware"></span><span id="upgrading-middleware"></span><h2>升级 Django 1.10 之前的中间件<a class="headerlink" href="#upgrading-pre-django-1-10-style-middleware" title="永久链接至标题">¶</a></h2>
<dl class="class">
<dt id="django.utils.deprecation.MiddlewareMixin">
<em class="property">class </em><code class="descclassname">django.utils.deprecation.</code><code class="descname">MiddlewareMixin</code><a class="headerlink" href="#django.utils.deprecation.MiddlewareMixin" title="永久链接至目标">¶</a></dt>
<dd></dd></dl>

<p>Django 提供了 <code class="docutils literal notranslate"><span class="pre">django.utils.deprecation.MiddlewareMixin</span></code> 来方便创建同时兼容 <a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a> 和旧的 <code class="docutils literal notranslate"><span class="pre">MIDDLEWARE_CLASSES</span></code> 的中间件类，并支持同步和异步请求。Django 所包含的所有中间件类都兼容这两种配置。</p>
<p>mixin 提供了一个 <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> 方法，它需要一个 <code class="docutils literal notranslate"><span class="pre">get_response</span></code> 参数，并将其存储在 <code class="docutils literal notranslate"><span class="pre">self.get_response</span></code> 中。</p>
<p><code class="docutils literal notranslate"><span class="pre">__call__()</span></code> 方法：</p>
<ol class="arabic simple">
<li>调用 <code class="docutils literal notranslate"><span class="pre">self.process_request(request)</span></code> （如果被定义过）。</li>
<li>调用 <code class="docutils literal notranslate"><span class="pre">self.get_response(request)</span></code> 来从后续的中间件和视图得到响应。</li>
<li>调用 <code class="docutils literal notranslate"><span class="pre">self.process_response(request,</span> <span class="pre">response)</span></code> （如果被定义过）。</li>
<li>返回响应。</li>
</ol>
<p>如果和 <code class="docutils literal notranslate"><span class="pre">MIDDLEWARE_CLASSES</span></code> 一起使用，<code class="docutils literal notranslate"><span class="pre">__call__()</span></code> 方法将永远不会被使用；Django 会直接调用 <code class="docutils literal notranslate"><span class="pre">process_request()</span></code> 和 <code class="docutils literal notranslate"><span class="pre">process_response()</span></code> 。</p>
<p>在大多数情况下，从这个 Mixin 中继承就足以使一个旧式中间件与新系统兼容，并具有足够的向后兼容性。新的短路语义对现有中间件无害甚至有益。在少数情况下，中间件类可能需要一些改变来适应新的语义。</p>
<p><a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a> 和 <code class="docutils literal notranslate"><span class="pre">MIDDLEWARE_CLASSES</span></code> 在使用上有些行为差异：</p>
<ol class="arabic simple">
<li><code class="docutils literal notranslate"><span class="pre">MIDDLEWARE_CLASSES</span></code> 下，每个中间件将始终调用它的 <code class="docutils literal notranslate"><span class="pre">process_response</span></code> 方法，即使早期的中间件通过从其 <code class="docutils literal notranslate"><span class="pre">process_response</span></code> 方法返回响应而短路。<a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a> 下，中间件行为更像洋葱：响应在输出时经过的层与在输入时看到请求的层相同。如果一个中间件短路，只有那个中间件和之前的中间件可以看到响应。</li>
<li>在 <code class="docutils literal notranslate"><span class="pre">MIDDLEWARE_CLASSES</span></code> 下，<code class="docutils literal notranslate"><span class="pre">process_exception</span></code> 应用于中间件 <code class="docutils literal notranslate"><span class="pre">process_request</span></code> 方法引发的异常。在 <a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a> 下，<code class="docutils literal notranslate"><span class="pre">process_exception</span></code> 只应用于视图引发的异常（或者从 <a class="reference internal" href="../../ref/template-response.html#django.template.response.TemplateResponse" title="django.template.response.TemplateResponse"><code class="xref py py-class docutils literal notranslate"><span class="pre">TemplateResponse</span></code></a> 的 <code class="docutils literal notranslate"><span class="pre">render</span></code> 方法引发的异常）。中间件引发的异常被转换为合适的 HTTP 响应，然后传递到下一个中间件。</li>
<li><code class="docutils literal notranslate"><span class="pre">MIDDLEWARE_CLASSES</span></code> 下，如果 <code class="docutils literal notranslate"><span class="pre">process_response</span></code> 方法引发了异常，所有更早之前的中间件的 <code class="docutils literal notranslate"><span class="pre">process_response</span></code> 方法会被跳过，并一直返回 <code class="docutils literal notranslate"><span class="pre">500</span> <span class="pre">Internal</span> <span class="pre">Server</span> <span class="pre">Error</span></code> 的 HTTP 响应（即使引发的异常是例如 <a class="reference internal" href="views.html#django.http.Http404" title="django.http.Http404"><code class="xref py py-class docutils literal notranslate"><span class="pre">Http404</span></code></a> ）。在 <a class="reference internal" href="../../ref/settings.html#std:setting-MIDDLEWARE"><code class="xref std std-setting docutils literal notranslate"><span class="pre">MIDDLEWARE</span></code></a> ，一个中间件引发的异常将立刻被转换为合适的 HTTP 响应，然后下一个中间件将看到响应。中间件不会因为中间件引发异常而被跳过。</li>
</ol>
<div class="versionchanged">
<span class="title">Changed in Django 3.1:</span> <p><code class="docutils literal notranslate"><span class="pre">MiddlewareMixin</span></code> 增加了对异步请求的支持。</p>
</div>
</div>
</div>


          </div>
        </div>
      </div>
      
        
          <div class="yui-b" id="sidebar">
            
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
  <h3><a href="../../contents.html">Table of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">中间件</a><ul>
<li><a class="reference internal" href="#writing-your-own-middleware">编写自己的中间件</a><ul>
<li><a class="reference internal" href="#init-get-response"><code class="docutils literal notranslate"><span class="pre">__init__(get_response)</span></code></a></li>
<li><a class="reference internal" href="#marking-middleware-as-unused">标记未使用的中间件</a></li>
</ul>
</li>
<li><a class="reference internal" href="#activating-middleware">激活中间件</a></li>
<li><a class="reference internal" href="#middleware-order-and-layering">中间件顺序与分层</a></li>
<li><a class="reference internal" href="#other-middleware-hooks">其他中间件钩子</a><ul>
<li><a class="reference internal" href="#process-view"><code class="docutils literal notranslate"><span class="pre">process_view()</span></code></a></li>
<li><a class="reference internal" href="#process-exception"><code class="docutils literal notranslate"><span class="pre">process_exception()</span></code></a></li>
<li><a class="reference internal" href="#process-template-response"><code class="docutils literal notranslate"><span class="pre">process_template_response()</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#dealing-with-streaming-responses">处理流式响应</a></li>
<li><a class="reference internal" href="#exception-handling">异常处理</a></li>
<li><a class="reference internal" href="#asynchronous-support">异步支持</a></li>
<li><a class="reference internal" href="#upgrading-pre-django-1-10-style-middleware">升级 Django 1.10 之前的中间件</a></li>
</ul>
</li>
</ul>

  <h4>上一个主题</h4>
  <p class="topless"><a href="generic-views.html"
                        title="上一章">通用视图</a></p>
  <h4>下一个主题</h4>
  <p class="topless"><a href="sessions.html"
                        title="下一章">如何使用会话</a></p>
  <div role="note" aria-label="source link">
    <h3>本页</h3>
    <ul class="this-page-menu">
      <li><a href="../../_sources/topics/http/middleware.txt"
            rel="nofollow">显示源代码</a></li>
    </ul>
   </div>
<div id="searchbox" style="display: none" role="search">
  <h3>快速搜索</h3>
    <div class="searchformwrapper">
    <form class="search" action="../../search.html" method="get">
      <input type="text" name="q" />
      <input type="submit" value="转向" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
    </div>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
              <h3>Last update:</h3>
              <p class="topless">12月 07, 2021</p>
          </div>
        
      
    </div>

    <div id="ft">
      <div class="nav">
    &laquo; <a href="generic-views.html" title="通用视图">previous</a>
     |
    <a href="../index.html" title="使用 Django" accesskey="U">up</a>
   |
    <a href="sessions.html" title="如何使用会话">next</a> &raquo;</div>
    </div>
  </div>

      <div class="clearer"></div>
    </div>
  </body>
</html>